using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Oops.Xna.Framework.Diagnostics
{
	/// <summary>
	/// Calculates and displays the frames-per-second.
	/// </summary>
	public class FrameRateComponent : DrawableGameComponent
	{
		private TimeSpan basisTime = TimeSpan.FromSeconds(1.0D);
		private float fps;
		private string fpsText = "";
		private int frameCount;
		private TimeSpan basisElapsed;

		private SpriteBatch spriteBatch;
		private SpriteFont spriteFont;

		/// <summary>
		/// Initializes a new instance of the <see cref="FrameRateComponent"/> class.
		/// </summary>
		/// <param name="game"><see cref="Microsoft.Xna.Framework.Game"/>that the game component should be attached to.</param>
		public FrameRateComponent(Game game) : base(game)
		{
			// The game parameter must be specified.
			if (game == null)
			{
				throw new ArgumentNullException("game");
			}

			// Default property values.
			this.Color = Color.White;
		}

		/// <summary>
		/// Gets or sets the location of the frame rate text.
		/// </summary>
		public Vector2 Position { get; set; }
		
		/// <summary>
		/// Gets or sets the color to use when displaying the frame rate.
		/// </summary>
		public Color Color { get; set; }
		
		/// <summary>
		/// Gets or sets the name of the <see cref="Microsoft.Xna.Framework.Graphics.SpriteFont"/> to use when displaying the frame rate.
		/// </summary>
		public string FontAssetName { get; set; }
		
		#region DrawableGameComponent Members

		protected override void LoadContent()
		{
			// Create the sprite batch.
			this.spriteBatch = new SpriteBatch(this.GraphicsDevice);

			// Get the font asset.
			this.spriteFont = this.Game.GetContent().Load<SpriteFont>(this.FontAssetName);
                        
			// Call inherited method.
			base.LoadContent();
		}

		protected override void UnloadContent()
		{
			// Dispose of the sprite batch.
			if (this.spriteBatch != null)
			{
				this.spriteBatch.Dispose();
			}
            
			// Call inherited method.
			base.UnloadContent();
		}

		#endregion

		#region IGameComponent Members

		public override void Initialize()
		{
			// Update and draw last.
			this.UpdateOrder = int.MaxValue;
			this.DrawOrder = int.MaxValue;
			
			// Call inherited method.
			base.Initialize();
		}

		#endregion

		#region IUpdateable Members

		public override void Update(GameTime gameTime)
		{
			// Increment the frame count.
			this.frameCount++;

			// Calcualate the frames per second.
			this.basisElapsed += gameTime.ElapsedGameTime;
			if (this.basisElapsed > this.basisTime)
			{
				this.fps = (float)this.frameCount / (float)this.basisElapsed.TotalSeconds;
				// TODO : GC!
				this.fpsText = this.fps.ToString();
				this.basisElapsed = TimeSpan.Zero;
				this.frameCount = 0;
			}

			// Call inherited method.	
			base.Update(gameTime);
		}

		#endregion

		#region IDrawable Members

		public override void Draw(GameTime gameTime)
		{
			// Draw the frame rate.
			this.spriteBatch.Begin(
				SpriteBlendMode.AlphaBlend, 
				SpriteSortMode.Immediate, 
				SaveStateMode.SaveState);
			this.spriteBatch.DrawString(
				this.spriteFont,
				this.fpsText,
				this.Position,
				this.Color);
			this.spriteBatch.End();

			// Call inherited method.
			base.Draw(gameTime);
		}

		#endregion
	}
}


